import Field from '@model/entity/Field'
import { getFieldName } from '@service/FieldService'
import { isArray, isEmpty, isFunction, isNumber, isString } from '@src/util/type'
import Log from '@src/util/log.ts'

export enum FieldArrayToObjectKeyEnum {
  Id = 'id',
  FieldName = 'fieldName',
  EnglishName = 'englishName',
  TagId = 'tagId',
}

/**
 * @description 取两个对象数组的交集
 * -- 临时写的，不是最优的方案
*/
export function objectArrayIntersection<T, U>(arr1: T[], arr2: U[], key: string = 'id', perferedLeft: boolean = false): any[] {
  if (arr1.length > arr2.length) return objectArrayIntersection(arr1, arr2, key, perferedLeft)
  
  let arr1Map: { [x: string]: T } = arr1.reduce((acc: any, item: any): any => {
    acc[item[key]] = item
    return acc
  }, {})
  
  let arr2Map: { [x: string]: U } = arr2.reduce((acc: any, item: any): any => {
    acc[item[key]] = item
    return acc
  }, {})
  
  let result: any[] = []
  
  for (let arr1Key in arr1Map) {
    let arr1MapItem: T = arr1Map[arr1Key]
    let arr2MapItem: U = arr2Map[arr1Key]
    
    if (arr2MapItem) {
      result.push(perferedLeft ? arr1MapItem : arr2MapItem)
      continue
    }
  }
  
  return result
}

function setIntersection<T>(set1: Set<T>, set2: Set<T>): T[] {
  if (set1.size > set2.size) return setIntersection(set2, set1)
  
  const intersection = new Set<T>()
  
  for (let item of set1) {
    if (set2.has(item)) {
      intersection.add(item)
    }
  }
  
  return [...intersection]
}

/** 
 * @description 取两个字符串数组的交集
 * -- 临时写的，不是最优的方案
*/
export function stringArrayIntersection(arr1: string[], arr2: string[]): string[] {
  const set1 = new Set<string>(arr1)
  const set2 = new Set<string>(arr2)
  
  return setIntersection<string>(set1, set2)
}

/**
 * @description: 字符串数组转对象, 对象值是索引
 * -- example： ['aa', ''bb] -> { 'aa': 'aa', 'bb': 'bb' }
 * @param {String[]} 字符串数组
 * @return {Record<string, number>} 转换后的对象, 对象值是索引
 */
export function stringArrayToObject(stringArray: string[]): Record<string, string> {
  return (
    stringArray.reduce(
      (acc: any, str: string) => (acc[str] = str) && acc,
      {}
    )
  )
}


/**
 * @description: 字符串数组转对象, 对象值是索引
 * -- example： ['aa', ''bb] -> { 'aa': 0, 'bb': 1 }
 * @param {String[]} 字符串数组
 * @return {Record<string, number>} 转换后的对象, 对象值是索引
 */
// TODO 和上面的方法差异在于返回值的格式，问题紧急先copy修改一份，后续修改
 export function stringArrayToObject2(stringArray: string[]): Record<string, string> {
  return (
    stringArray.reduce(
      (acc: any, str: string, currentIndex: number) => {
        acc[str] = currentIndex
        return acc
      },
      {}
    )
  )
}

/**
 * @description: 字段数组转对象, 对象值是字段名称 fieldName
 * @param {Field[]} fieldArray
 * @return {Record<string, Field>} { [fieldName]: Field }
 */
export function fieldArrayToObject<T = Field>(
  fieldArray: T[], 
  key: FieldArrayToObjectKeyEnum | Function = FieldArrayToObjectKeyEnum.FieldName
): Record<string, T> {
  
  if (!isArray(fieldArray)) return {}
  
  const getObjectKey = (field: T) => {
    if (isFunction(key)) {
      let getKeyFunction = key as Function
      return getKeyFunction(field) 
    }
    
    if (key == FieldArrayToObjectKeyEnum.FieldName) {
      return getFieldName(field)
    }
    
    // @ts-ignore
    return field?.[key]
  }
  
  let fieldKey: string = ''
  
  return (
    fieldArray.reduce(
      (acc: Record<string, T>, field: T) => {
        fieldKey = getObjectKey(field);
        if (fieldKey){
          (acc[fieldKey] = field)
        }
        return acc
      },
      {}
    )
  )
}

export function arrayToObject<Type extends Record<string, any>>(arr: Type[], key: string): Record<string, Type> {
  const obj: Record<string, Type> = {}
  
  arr.forEach((item: Type) => {
    obj[item[key]] = item
  })
  
  return obj
}

export function getArrayFirstValue<Type, DefaultValueType>(arr: Type[], defaultValue: any): Type | DefaultValueType {
  try {
    
    if (!isArray(arr) || isEmpty(arr)) {
      return defaultValue
    }
    
    return arr[0]
    
  } catch (error) {
    Log.error(error, getArrayFirstValue.name)
    return defaultValue
  }
}

export function isStringArray(value: any[]): boolean {
  return isArray(value) && value.every(item => isString(item))
}